package model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Observable;

import connectors.DataSource;
import connectors.impl.BinaryConnector;
import controller.Controller;

public class Model extends Observable {

	private DataSource src;
	private File dataFile = new File("DailySpendings.data");
	private File settingsFile = new File("DailySpendings.config");

	public Model(Controller dailySpendings) {
		
		if(settingsFile.exists()) readSettings();
		
		openDataSource();
	}

	private void openDataSource() {
		src = new BinaryConnector();
		src.initialize(dataFile);
	}

	public double getDayExpenseSum(int year, int month, int day,
			Integer category, Integer account) {
		return sum(src.getDayExpenses(year, month, day, category, account));
	}

	public double getDayIncomeSum(int year, int month, int day, Integer account) {
		return sum(src.getDayIncome(year, month, day, account));
	}

	public double getMonthExpenseSum(int year, int month, Integer category,
			Integer account) {
		return sum(src.getMonthExpenses(year, month, category, account));
	}

	public double getMonthIncomeSum(int year, int month, Integer account) {
		return sum(src.getMonthIncome(year, month, account));
	}

	public Object getMonthBalance(int year, int month, Integer category,
			Integer account) {
		return getMonthIncomeSum(year, month, account)
				- getMonthExpenseSum(year, month, category, account);
	}

	public double getYearExpenseSum(int year, Integer category, Integer account) {
		return sum(src.getYearExpenses(year, category, account));
	}

	public double getYearIncomeSum(int year, Integer account) {
		return sum(src.getYearIncome(year, account));
	}

	public double getYearBalance(int year, Integer category, Integer account) {
		return getYearIncomeSum(year, account)
				- getYearExpenseSum(year, category, account);
	}

	public static double sum(LinkedList<? extends Transaction> transactions) {
		double amount = 0.0;

		for (Transaction transfer : transactions) {
			amount += transfer.getAmount();
		}

		return amount;
	}

	public Object[] getCoveredYears() {
		Object[] years = src.getCoveredYears();
		Arrays.sort(years);

		return years;
	}

	public DataSource getDataSource() {
		return src;
	}
	
	public void addExpense(Expense exp) {
		getDataSource().addExpense(exp);
		fireChanges();
	}
	
	public void addIncome(Income income) {
		getDataSource().addIncome(income);
		fireChanges();
	}
	
	public void addTransfer(Transfer transfer) {
		getDataSource().addTransfer(transfer);
		fireChanges();
	}
	
	public void removeTransaction(Object transaction) {
		if(transaction instanceof Expense) {
			getDataSource().removeExpense((Expense) transaction);
			System.out.println("removed expense");
		}
		else if(transaction instanceof Income) {
			getDataSource().removeIncome((Income) transaction);
			System.out.println("removed expense");
		}
		// TODO implement rest
		
		fireChanges();
	}
	
	private void fireChanges() {
		setChanged();
		notifyObservers();		
	}
	
	public void store() {
		saveSettings();
		
		src.store(dataFile);
	}
	
	public void openDataFile(File dataFile) {
		this.dataFile = dataFile;
		src.initialize(dataFile);
		fireChanges();
	}

	private void saveSettings() {
		OutputStream fos = null;

		try {
			fos = new FileOutputStream(settingsFile);
			ObjectOutputStream o = new ObjectOutputStream(fos);

			o.writeObject(dataFile);
			
			o.close();
			
		} catch (IOException e) {
			System.err.println(e);
		} finally {
			try {
				fos.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public void readSettings() {
		InputStream fis = null;

		try {
			fis = new FileInputStream(settingsFile);

			ObjectInputStream o = new ObjectInputStream(fis);
			
			dataFile = (File) o.readObject();
			
			o.close();			
		} catch (IOException e) {
			System.err.println("IOException");
		} catch (ClassNotFoundException e) {
			System.err.println("ClassNotFoundException");
		} finally {
			try {
				fis.close();
			} catch (Exception e) {
				System.err.println("Closing filestream failed");
			}
		}
		
	}

	public void saveTo(File newDataFile) {
		dataFile = newDataFile;
		store();		
	}
	
}
